import tkinter as tk
from tkinter import ttk, messagebox
import cv2
import os
import glob
import threading
import time
from datetime import datetime
from PIL import Image, ImageTk
import json

class PasswordDialog(tk.Toplevel):
    """
    密码验证对话框类
    提供数字键盘输入密码功能
    """
    def __init__(self, parent, config, title="请输入密码"):
        super().__init__(parent)
        self.parent = parent
        self.title(title)
        self.geometry("300x400")
        self.resizable(False, False)
        self.transient(parent)  # 设置为主窗口的子窗口
        self.grab_set()  # 模态窗口
        
        self.password = ""  # 存储输入的密码
        self.result = False  # 验证结果
        self.correct_password = config.get("password", "892186")  # 从配置获取正确密码
        
        # 创建界面元素
        self.create_widgets()
        
        # 居中显示
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.parent.winfo_width() // 2) - (width // 2) + self.parent.winfo_x()
        y = (self.parent.winfo_height() // 2) - (height // 2) + self.parent.winfo_y()
        self.geometry(f"{width}x{height}+{x}+{y}")
    
    def create_widgets(self):
        """创建密码对话框的界面元素"""
        # 密码显示区域
        self.password_var = tk.StringVar(value="*****")
        password_frame = ttk.Frame(self, padding="20 10 20 10")
        password_frame.pack(fill=tk.X)
        
        password_label = ttk.Label(password_frame, textvariable=self.password_var, font=("Arial", 16))
        password_label.pack()
        
        # 数字键盘区域
        keypad_frame = ttk.Frame(self, padding="10")
        keypad_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建数字按钮
        buttons = [
            '1', '2', '3',
            '4', '5', '6',
            '7', '8', '9',
            '清空', '0', '删除'
        ]
        
        row, col = 0, 0
        for button_text in buttons:
            if button_text == '清空':
                btn = ttk.Button(keypad_frame, text=button_text, width=8, command=self.clear_password)
            elif button_text == '删除':
                btn = ttk.Button(keypad_frame, text=button_text, width=8, command=self.delete_char)
            else:
                btn = ttk.Button(keypad_frame, text=button_text, width=8, command=lambda text=button_text: self.add_char(text))
            
            btn.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
            
            col += 1
            if col > 2:
                col = 0
                row += 1
        
        # 设置网格权重使按钮均匀分布
        for i in range(4):
            keypad_frame.grid_rowconfigure(i, weight=1)
        for i in range(3):
            keypad_frame.grid_columnconfigure(i, weight=1)
        
        # 确定和取消按钮
        button_frame = ttk.Frame(self, padding="10")
        button_frame.pack(fill=tk.X)
        
        cancel_btn = ttk.Button(button_frame, text="取消", command=self.cancel)
        cancel_btn.pack(side=tk.RIGHT, padx=5)
        
        confirm_btn = ttk.Button(button_frame, text="确定", command=self.confirm)
        confirm_btn.pack(side=tk.RIGHT, padx=5)
    
    def add_char(self, char):
        """添加一个字符到密码"""
        if len(self.password) < 6:  # 限制密码长度为6位
            self.password += char
            self.update_password_display()
    
    def delete_char(self):
        """删除最后一个字符"""
        if self.password:
            self.password = self.password[:-1]
            self.update_password_display()
    
    def clear_password(self):
        """清空密码"""
        self.password = ""
        self.update_password_display()
    
    def update_password_display(self):
        """更新密码显示"""
        if self.password:
            display = '*' * len(self.password)
        else:
            display = "*****"
        self.password_var.set(display)
    
    def confirm(self):
        """确认按钮处理"""
        if self.password == self.correct_password:
            self.result = True
            self.destroy()
        else:
            self.password_var.set("密码错误!")
            self.after(1000, self.update_password_display)  # 1秒后恢复显示
    
    def cancel(self):
        """取消按钮处理"""
        self.result = False
        self.destroy()
    
    def show(self):
        """显示对话框并返回验证结果"""
        self.wait_window()
        return self.result

class QRVisualizer:
    """
    二维码可视化轮询播放界面
    用于展示生成的二维码，支持设置播放时间间隔
    """
    
    def __init__(self, root, config=None):
        """
        初始化可视化界面
        
        参数:
            root: tkinter根窗口
            config: 配置字典
        """
        self.root = root
        
        # 加载配置文件
        if config is None:
            self.config = self.load_config()
        else:
            self.config = config
        
        # 从配置获取参数
        self.qr_config = self.config.get("qr_visualizer", {})
        self.folder_path = self.qr_config.get("folder_path", "qr_output")
        self.display_time_value = self.qr_config.get("display_time", 2)
        self.work_time_enabled_value = self.qr_config.get("work_time_enabled", True)
        self.work_start_hour_value = self.qr_config.get("work_start_hour", 8)
        self.work_end_hour_value = self.qr_config.get("work_end_hour", 18)
        
        # 设置窗口属性
        self.root.title(self.qr_config.get("window_title", "中环信科编码工具"))
        self.root.geometry(self.qr_config.get("window_size", "800x480"))
        self.root.configure(bg="#f0f0f0")
        
        # 状态变量
        self.is_playing = False
        self.play_thread = None
        self.stop_event = threading.Event()
        self.image_files = []
        self.current_index = 0
        
        # 新增：运行时间和统计信息
        self.start_time = datetime.now()  # 程序开始运行时间
        self.total_transferred = 0  # 累计传输的数据条数（图片数）
        self.current_task_hash = None  # 当前任务哈希
        self.last_task_time = None  # 最近一次扫描到新任务的时间
        
        # 保留基本状态变量（用于内部监控）
        self.is_normal_state = True  # 默认状态为正常
        
        # 创建界面元素
        self.create_widgets()
        
        # 检查默认文件夹
        self.check_folder()
        
        # 绑定窗口关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # 绑定窗口调整大小事件
        self.root.bind("<Configure>", self.on_window_resize)
        
        # 启动状态监控
        self.start_monitoring()
    
    def load_config(self):
        """从配置文件加载配置"""
        config_path = os.path.join(os.path.dirname(__file__), "config.json")
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            messagebox.showerror("配置错误", f"无法加载配置文件: {e}")
            # 返回默认配置
            return {
                "qr_visualizer": {
                    "display_time": 2,
                    "work_time_enabled": True,
                    "work_start_hour": 8,
                    "work_end_hour": 18,
                    "folder_path": "qr_output",
                    "window_title": "中环信科编码工具",
                    "window_size": "800x480",
                    "password": "892186"
                }
            }
    
    def create_widgets(self):
        """创建所有界面控件 - 最大化显示二维码"""
        # 创建主容器，确保它占满整个窗口
        main_container = ttk.Frame(self.root)
        main_container.pack(fill=tk.BOTH, expand=True)
        
        # 设置主容器的权重，确保它能正确拉伸
        main_container.grid_rowconfigure(0, weight=1)
        main_container.grid_columnconfigure(0, weight=1)
        
        # 直接在主容器中放置图片标签，居中显示，无边框
        self.image_label = ttk.Label(main_container, text="正在加载二维码...", font=("Arial", 10))
        self.image_label.grid(row=0, column=0, sticky="", padx=5, pady=5)
    
    def browse_folder(self):
        """文件夹选择功能已移除，使用默认的qr_output文件夹"""
        pass
    
    def check_folder(self):
        """检查默认文件夹并加载二维码图片"""
        try:
            if not os.path.exists(self.folder_path):
                messagebox.showwarning("警告", f"默认文件夹 '{self.folder_path}' 不存在")
                self.set_error_state()
                return False
            
            # 支持的图片格式
            image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.tiff', '*.tif', '*.webp']
            
            # 获取所有图片文件
            self.image_files = []
            for extension in image_extensions:
                self.image_files.extend(glob.glob(os.path.join(self.folder_path, extension)))
            
            if not self.image_files:
                messagebox.showwarning("警告", f"在默认文件夹 '{self.folder_path}' 中没有找到图片文件")
                self.set_error_state()
                return False
            
            # 按文件名排序
            self.image_files.sort()
            self.current_index = 0
            
            # 设置为正常状态
            self.set_normal_state()
            return True
        except Exception as e:
            print(f"检查文件夹时出错: {str(e)}")
            self.set_error_state()
            return False
    
    def toggle_play(self):
        """切换播放状态"""
        if self.is_playing:
            # 停止播放需要密码验证
            password_dialog = PasswordDialog(self.root, self.qr_config, title="请输入密码以停止播放")
            if password_dialog.show():
                self.stop_play()
        else:
            self.start_play()
    
    def start_play(self):
        """开始播放"""
        if not self.check_folder():
            return
        
        self.is_playing = True
        # 重置停止事件
        self.stop_event.clear()
        
        # 更新累计传输数据条数
        self.total_transferred = len(self.image_files)
        if hasattr(self, 'total_transferred_var'):
            self.total_transferred_var.set(str(self.total_transferred)+" 条")
        
        # 检查是否有新任务（基于文件夹中的图片是否存在）
        if self.image_files:
            # 从文件名提取任务哈希（假设文件名格式为hash_chunk_xxx.png）
            first_file = os.path.basename(self.image_files[0])
            if '_chunk_' in first_file:
                new_task_hash = first_file.split('_chunk_')[0]
                if new_task_hash != self.current_task_hash:
                    self.current_task_hash = new_task_hash
                    self.last_task_time = datetime.now()
                    if hasattr(self, 'last_task_time_var'):
                        self.last_task_time_var.set(self.last_task_time.strftime("%Y-%m-%d %H:%M:%S"))
        
        # 启动运行时间更新定时器
        self.update_run_time()
        
        # 创建并启动播放线程
        self.play_thread = threading.Thread(target=self.play_slideshow, daemon=True)
        self.play_thread.start()
    
    def stop_play(self):
        """停止播放"""
        self.is_playing = False
        # 设置停止事件
        self.stop_event.set()
        
        if self.play_thread and self.play_thread.is_alive():
            self.play_thread.join(timeout=1.0)
    
    def start_monitoring(self):
        """启动程序状态监控"""
        # 定时检查程序状态
        self.root.after(5000, self._check_program_status)
    
    def _check_program_status(self):
        """内部方法：检查程序状态"""
        try:
            # 检查关键状态
            status_ok = True
            
            # 检查图片文件列表是否为空
            if not self.image_files:
                status_ok = False
            
            # 检查播放线程是否正常运行（如果应该在播放）
            if self.is_playing and (self.play_thread is None or not self.play_thread.is_alive()):
                status_ok = False
            
            # 检查文件夹是否存在
            if not os.path.exists(self.folder_path):
                status_ok = False
            
            # 根据检查结果更新状态
            if status_ok:
                self.set_normal_state()
            else:
                self.set_error_state()
                # 尝试重新加载文件夹
                if not self.image_files or not os.path.exists(self.folder_path):
                    self.check_folder()
        except Exception as e:
            print(f"检查程序状态时出错: {str(e)}")
            self.set_error_state()
        
        # 继续定时检查
        self.root.after(5000, self._check_program_status)
    
    def play_slideshow(self):
        """轮播播放线程函数"""
        try:
            while self.is_playing and not self.stop_event.is_set():
                try:
                    # 检查是否在工作时间内
                    if self.is_in_work_time():
                        self.display_current_image()
                    else:
                        # 工作时间外，显示提示信息
                        if hasattr(self, 'info_var'):
                            def update_info(): 
                                self.info_var.set("当前非工作时间，暂停播放")
                            self.root.after(0, update_info)
                    
                    # 等待指定时间或直到停止事件被设置
                    for _ in range(self.display_time_value * 10):  # 分解为0.1秒的小步
                        if self.stop_event.is_set():
                            break
                        time.sleep(0.1)
                    
                    if self.stop_event.is_set():
                        break
                    
                    # 更新索引到下一张图片
                    self.current_index = (self.current_index + 1) % len(self.image_files)
                    
                except IndexError:
                    # 索引错误通常意味着image_files为空
                    print("图片列表为空，无法继续播放")
                    self.set_error_state()
                    # 尝试重新加载文件夹
                    if self.check_folder():
                        self.current_index = 0
                    else:
                        # 如果仍然没有图片，等待一段时间后重试
                        time.sleep(2)
                except Exception as e:
                    print(f"播放幻灯片时出错: {str(e)}")
                    self.set_error_state()
                    # 短暂暂停后继续
                    time.sleep(1)
        except Exception as e:
            print(f"播放线程异常: {str(e)}")
            self.root.after(0, lambda: self.set_error_state())
    
    def display_current_image(self):
        """显示当前图片 - 调整为适应窗口尺寸"""
        if not self.image_files:
            self.set_error_state()
            return
        
        try:
            image_path = self.image_files[self.current_index]
            
            # 获取窗口尺寸
            root_width = self.root.winfo_width()
            root_height = self.root.winfo_height()
            
            # 如果尺寸为0（可能还未渲染完成），使用默认值
            if root_width <= 10 or root_height <= 10:
                root_width = 800
                root_height = 600
            
            # 读取图片
            img = cv2.imread(image_path)
            if img is None:
                error_msg = f"无法读取图片: {image_path}"
                print(error_msg)
                self.root.after(0, lambda: messagebox.showerror("错误", error_msg))
                self.set_error_state()
                return
            
            # 获取图片尺寸
            height, width = img.shape[:2]
            
            # 计算缩放比例，保持纵横比，使图片尽可能最大化显示
            # 只留出很小的边距
            target_width = root_width - 20  # 最小化左右边距
            target_height = root_height - 20  # 最小化上下边距
            
            # 计算缩放比例，使用较小的比例以确保图片完全适应
            scale = min(target_width / width, target_height / height)
            new_width = int(width * scale)
            new_height = int(height * scale)
            
            # 调整图片大小
            img = cv2.resize(img, (new_width, new_height))
            
            # 转换BGR为RGB
            img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            
            # 转换为PIL图像
            pil_img = Image.fromarray(img_rgb)
            
            # 转换为PhotoImage
            photo = ImageTk.PhotoImage(image=pil_img)
            
            # 更新界面
            self.root.after(0, lambda p=photo, path=image_path, idx=self.current_index: self.update_image_display(p, path, idx))
            
            # 显示成功，设置为正常状态
            self.set_normal_state()
            
        except Exception as e:
            error_msg = f"显示图片时出错: {str(e)}"
            print(error_msg)
            self.root.after(0, lambda msg=error_msg: messagebox.showerror("错误", msg))
            self.set_error_state()
    
    def update_run_time(self):
        """更新程序运行时间"""
        # 简化版本，不更新UI显示
        pass
    
    def start_error_flashing(self):
        """开始错误状态处理（无边框闪烁）"""
        pass
    
    def stop_error_flashing(self):
        """停止错误状态处理"""
        pass
    
    def set_normal_state(self):
        """设置为正常状态（内部状态，无边框显示）"""
        self.is_normal_state = True
    
    def set_error_state(self):
        """设置为错误状态（内部状态，无边框显示）"""
        self.is_normal_state = False
    
    def update_image_display(self, photo, image_path, index):
        """更新图片显示，包括状态边框"""
        self.image_label.config(image=photo)
        self.image_label.image = photo  # 保存引用防止被垃圾回收
    
    # prev_image 和 next_image 方法已移除
    
    def on_closing(self):
        """处理窗口关闭事件，添加密码验证功能"""
        # 创建密码对话框
        password_dialog = PasswordDialog(self.root, self.config, title="请输入密码以关闭窗口")
        
        # 显示对话框并获取验证结果
        # 由于PasswordDialog.show()方法似乎没有完全实现，我们使用事件循环等待对话框关闭
        self.root.wait_window(password_dialog)
        
        # 只有密码正确才允许关闭窗口
        if password_dialog.result:
            # 停止播放
            if self.is_playing:
                self.stop_play()
            self.root.destroy()
    
    def on_work_time_toggle(self):
        """处理工作时间启用状态变化"""
        # 控制时间选择框的可用性（仅控制小时选择框）
        enabled = self.work_time_enabled.get()
        self.start_hour_spinbox.config(state='normal' if enabled else 'disabled')
        self.end_hour_spinbox.config(state='normal' if enabled else 'disabled')
    
    def apply_work_time_settings(self):
        """应用工作时间设置（仅基于小时）"""
        password_dialog = PasswordDialog(self.root, self.qr_config, title="请输入密码以应用工作时间设置")
        if password_dialog.show():
            messagebox.showinfo("成功", f"工作时间设置已应用：{self.work_start_hour_value:02d} 时 - {self.work_end_hour_value:02d} 时")
    
    def is_in_work_time(self):
        """
        检查当前时间是否在工作时间内（仅基于小时）
        
        返回:
            bool: 如果在工作时间内返回True，否则返回False
        """
        # 如果未启用工作时间限制，返回True
        if not self.work_time_enabled_value:
            return True
        
        # 获取当前时间
        now = datetime.now()
        current_hour = now.hour
        
        start_hour = self.work_start_hour_value
        end_hour = self.work_end_hour_value
        
        # 判断是否在工作时间内（考虑跨天情况）
        if start_hour <= end_hour:
            # 同一天内（如8-18时）
            return start_hour <= current_hour <= end_hour
        else:
            # 跨天情况（如20-6时）
            return current_hour >= start_hour or current_hour <= end_hour
        

    
    def on_window_resize(self, event):
        """窗口大小改变时重新调整图片大小"""
        # 避免递归调用
        if event.widget == self.root and self.is_playing and self.image_files:
            # 使用after在主循环中延迟执行，避免频繁调整
            self.root.after(100, self.display_current_image)

# 主程序入口
if __name__ == "__main__":
    root = tk.Tk()
    app = QRVisualizer(root)
    # 自动开始播放
    root.after(1000, app.start_play)
    root.mainloop()